//==============================================================================
//
//     DGraphicScreen - the graphic screen wrapper class in the ofc-library
//
//               Copyright (C) 2007  Dick van Oudheusden
//  
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this library; if not, write to the Free
// Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
//==============================================================================
// 
//  $Date: 2008-08-26 05:50:30 $ $Revision: 1.18 $
//
//==============================================================================

#include "ofc/DGraphicScreen.h"

#ifdef HAVE_DGRAPHICSCREEN
#include "SDL.h"
#endif

#include <stdlib.h>
#include <stdio.h>
#include <math.h>

#include "ofc/DAtExit.h"


#if _INTERFACE_

#include "ofc/config.h"

#include "ofc/DColor.h"

#include "ofc/DScreenHandler.h"
#include "ofc/DDrawable.h"

#include "ofc/DFont.h"
#include "ofc/DSimpleFont.h"
#include "ofc/DImage.h"


#if defined(HAVE_SDL)
# define HAVE_DGRAPHICSCREEN (1)
#endif



#ifdef HAVE_DGRAPHICSCREEN

//
// The DGraphicDrawable class implements a class with the shared methods between
// the graphic screen class and the graphic surface class. It contains the methods
// for drawing, clipping, color.. The x and y positions start from zero, so the
// screen size is 0..maxX and 0..maxY (inclusive).
// ToDo: drawing boxes, circles..
// 

@interface DGraphicDrawable : Object <DDrawable>
{
@protected
  unsigned             _maxX;        // the right x-position of the drawable
  unsigned             _maxY;        // the bottom y-position of the drawable
  
  DColor              *_fgc;         // the current foreground color
  DColor              *_bgc;         // the current background color
  id <DFont>           _font;        // the current font
  DSimpleFont         *_basicFont;   // the basic font
  
  void                *_surface;     // the surface of the drawable
  
@private
  BOOL                 _drawing;     // is drawing active ?
  
  unsigned             _clipMinX;    // the left x-position for the clip area
  unsigned             _clipMaxX;    // the right x-position for the clip area
  unsigned             _clipMinY;    // the top y-position for the clip area
  unsigned             _clipMaxY;    // the bottom y-position for the clip area
  unsigned             _cursorX;     // the x-position of the cursor
  unsigned             _cursorY;     // the y-position of the cursor
  unsigned             _nextY;       // the next y-position after writing text
  
  int                  _lineType;    // the last line type
  int                  _dashLength;  // the length of a dash
  int                  _bezierSteps; // the number of iteration steps for a bezier curve (def. 15)
}

#endif
#endif


#ifdef HAVE_DGRAPHICSCREEN

#if _PRIVATE_

//
// Convert the text color to the sdl color
// 
// @param color     the text color
// 
// @return the curses color
// 
static Uint32 _color2SDL(SDL_Surface *surface, DColor *color)
{
  return SDL_MapRGB(surface->format, [color red], [color green], [color blue]);
}


//
// Put a pixel on the screen
//
// @param x       the x-position
// @param y       the y-position 
// @param color   the attributes
// @param alpha   the alpha factor
// 
// @return success
// 
static BOOL _putPixel(SDL_Surface *surface, unsigned x, unsigned y, Uint32 color, Uint8 alpha)
{
	switch (surface->format->BytesPerPixel) 
  {
    case 1: // 8 bits per pixel
    {
      if (alpha == 255) 
      {
		    *((Uint8 *) surface->pixels + y * surface->pitch + x) = color;
      } 
      else 
      {
        Uint8 *addr  = (Uint8 *) surface->pixels + y * surface->pitch + x;
        Uint8  pixel = *addr;
        
		    Uint8 dR = surface->format->palette->colors[pixel].r;
		    Uint8 dG = surface->format->palette->colors[pixel].g;
		    Uint8 dB = surface->format->palette->colors[pixel].b;
        
		    Uint8 sR = surface->format->palette->colors[color].r;
		    Uint8 sG = surface->format->palette->colors[color].g;
		    Uint8 sB = surface->format->palette->colors[color].b;

		    dR = dR + ((sR - dR) * alpha >> 8);
		    dG = dG + ((sG - dG) * alpha >> 8);
		    dB = dB + ((sB - dB) * alpha >> 8);

		    *addr = SDL_MapRGB(surface->format, dR, dG, dB);
      }
    }
    break;

    case 2: // 16 bits per pixel
    {
      if (alpha == 255) 
      {
		    *((Uint16 *) surface->pixels + y * surface->pitch / 2 + x) = color;
      } 
      else 
      {
		    Uint16 *addr  = (Uint16 *) surface->pixels + y * surface->pitch / 2 + x;
        Uint32  pixel = *addr;
        
        Uint32 Rmask = surface->format->Rmask;
        Uint32 Gmask = surface->format->Gmask;
        Uint32 Bmask = surface->format->Bmask;
        Uint32 Amask = surface->format->Amask;

		    Uint32 R = ((pixel & Rmask) + (((color & Rmask) - (pixel & Rmask)) * alpha >> 8)) & Rmask;
		    Uint32 G = ((pixel & Gmask) + (((color & Gmask) - (pixel & Gmask)) * alpha >> 8)) & Gmask;
		    Uint32 B = ((pixel & Bmask) + (((color & Bmask) - (pixel & Bmask)) * alpha >> 8)) & Bmask;
        Uint32 A = 0;
        
		    if (Amask)
        {
          A = ((pixel & Amask) + (((color & Amask) - (pixel & Amask)) * alpha >> 8)) & Amask;
        }
        
		    *addr = R | G | B | A;
      }
    }
    break;

    case 3: // 24 bits per pixel
    {
      Uint8 *addr = (Uint8 *) surface->pixels + y * surface->pitch + x * 3;
      
      Uint8 Rshift8 = surface->format->Rshift / 8;
      Uint8 Gshift8 = surface->format->Gshift / 8;
      Uint8 Bshift8 = surface->format->Bshift / 8;
      Uint8 Ashift8 = surface->format->Ashift / 8;

      if (alpha == 255) 
      {
		    *(addr + Rshift8) = color >> surface->format->Rshift;
		    *(addr + Gshift8) = color >> surface->format->Gshift;
		    *(addr + Bshift8) = color >> surface->format->Bshift;
		    *(addr + Ashift8) = color >> surface->format->Ashift;
      } 
      else 
      {
		    Uint8 dR = *((addr) + Rshift8);
		    Uint8 dG = *((addr) + Gshift8);
		    Uint8 dB = *((addr) + Bshift8);
		    Uint8 dA = *((addr) + Ashift8);

		    Uint8 sR = (color >> surface->format->Rshift) & 0xff;
		    Uint8 sG = (color >> surface->format->Gshift) & 0xff;
		    Uint8 sB = (color >> surface->format->Bshift) & 0xff;
		    Uint8 sA = (color >> surface->format->Ashift) & 0xff;

		    dR = dR + ((sR - dR) * alpha >> 8);
		    dG = dG + ((sG - dG) * alpha >> 8);
		    dB = dB + ((sB - dB) * alpha >> 8);
		    dA = dA + ((sA - dA) * alpha >> 8);

		    *((addr) + Rshift8) = dR;
		    *((addr) + Gshift8) = dG;
		    *((addr) + Bshift8) = dB;
		    *((addr) + Ashift8) = dA;
      }
    }
    break;

    case 4: // 32 bits per pixel
    {
      if (alpha == 255) 
      {
		    *((Uint32 *) surface->pixels + y * surface->pitch / 4 + x) = color;
      }
      else 
      {
		    Uint32 *addr  = (Uint32 *) surface->pixels + y * surface->pitch / 4 + x;
		    Uint32  pixel = *addr;

		    Uint32 Rshift = surface->format->Rshift;
		    Uint32 Gshift = surface->format->Gshift;
		    Uint32 Bshift = surface->format->Bshift;
		    Uint32 Ashift = surface->format->Ashift;

        Uint32 Rmask = surface->format->Rmask;
        Uint32 Gmask = surface->format->Gmask;
        Uint32 Bmask = surface->format->Bmask;
        Uint32 Amask = surface->format->Amask;

		    Uint32 R = ((pixel & Rmask) + (((((color & Rmask) - (pixel & Rmask)) >> Rshift) * alpha >> 8) << Rshift)) & Rmask;
		    Uint32 G = ((pixel & Gmask) + (((((color & Gmask) - (pixel & Gmask)) >> Gshift) * alpha >> 8) << Gshift)) & Gmask;
		    Uint32 B = ((pixel & Bmask) + (((((color & Bmask) - (pixel & Bmask)) >> Bshift) * alpha >> 8) << Bshift)) & Bmask;
        Uint32 A = 0;
        
		    if (Amask)
        {
          A = ((pixel & Amask) + (((((color & Amask) - (pixel & Amask)) >> Ashift) * alpha >> 8) << Ashift)) & Amask;
        }

		    *addr = R | G | B | A;
      }
    }
    break;
  }
  
  return YES;
}


//
// Get a pixel from the screen
//
// @param surface the surface info
// @param x       the x-position
// @param y       the y-position 
// 
// @return the color 
// 
static Uint32 _getPixel(SDL_Surface *surface, unsigned x, unsigned y)
{
	switch (surface->format->BytesPerPixel) 
  {
    case 1: // 8 bits per pixel
    {
      return (Uint32)(*((Uint8 *) surface->pixels + y * surface->pitch + x));
    }
    break;

    case 2: // 16 bits per pixel
    {
      return (Uint32)(*((Uint16 *) surface->pixels + y * surface->pitch / 2 + x));
    } 
    break;

    case 3: // 24 bits per pixel
    {
      Uint8 *addr = (Uint8 *) surface->pixels + y * surface->pitch + x * 3;
      
      Uint8 Rshift = surface->format->Rshift;
      Uint8 Gshift = surface->format->Gshift;
      Uint8 Bshift = surface->format->Bshift;

      return (Uint32)(*(addr + Rshift / 8) << Rshift) | (Uint32)(*(addr + Gshift / 8) << Gshift) | (Uint32)(*(addr + Bshift / 8) << Bshift);
    }
    break;

    case 4: // 32 bits per pixel
    {
      return (*((Uint32 *) surface->pixels + y * surface->pitch / 4 + x));
    }
    break;
  }
  return 0;
}

#endif


@implementation DGraphicDrawable

//
// Initialise an graphic drawable object
//
// @return the object
//
- (DGraphicDrawable *) init
{
  [super init];
  
  _clipMinX    = 0;
  _clipMaxX    = 0;
  _clipMinY    = 0;
  _clipMaxY    = 0;
  
  _cursorX     = 0;
  _cursorY     = 0;
  
  _maxX        = 0;
  _maxY        = 0;

  _surface     = NULL;

  _fgc         = [DColor new];
  _bgc         = [DColor new];

  _font        = nil;
  _basicFont   = [DSimpleFont new];
  
  _drawing     = NO;

  _lineType    = DDW_LINE_SOLID;
  _dashLength  = 10;
  _bezierSteps = 15;

  return self;
}


//// Copy related methods

//
// Do a shallow copy of the graphic drawable object
//
// @return the object
//
- shallowCopy
{
  DGraphicDrawable *copy = [super shallowCopy];
  
  copy->_fgc       = [_fgc copy];
  copy->_bgc       = [_bgc copy];

  copy->_basicFont = [_basicFont copy];
  
  return copy;
}


//// Deconstructor

//
// Free the graphic screen
// 
// @return the object
// 
- free
{
  [_fgc free];
  [_bgc free];

  [_basicFont free];
  
  return [super free];
}


//// Member methods

//
// Check if the graphic drawable object is open
// 
// @return is it ?
//
- (BOOL) isOpen
{
  return (_surface != NULL);
}


//
// Return the number of bezier iteration steps (def. 15)
// 
// @return the number of steps
// 
- (int) bezierSteps
{
  return _bezierSteps;
}

//
// Set the number of bezier iteration steps
// 
// @param steps    the number of bezier steps (>1)
// 
// @return the object
// 
- (DGraphicDrawable *) bezierSteps :(int) steps
{
  if (steps < 2)
    WARNING(DW_INVALID_ARG, "steps");
  else
    _bezierSteps = steps;
  
  return self;
}

//
// Return the current line type
// 
// @return the line type (DDW_LINE_SOLID,DDW_LINE_DASHED,DDW_LINE_DOTTED)
// 
- (int) lineType
{
  return _lineType;
}

//
// Set the current line type
// 
// @param type     the line type (DDW_LINE_SOLID,DDW_LINE_DASHED,DDW_LINE_DOTTED)
// 
// @return the object
// 

- (DGraphicDrawable *) lineType :(int) type
{
  if ((type < DDW_LINE_SOLID) || (type > DDW_LINE_DOTTED))
    WARNING(DW_INVALID_ARG, "type");
  else
    _lineType = type;

  return self;
}

//
// Return the length of a dash (def. 10)
// 
// @return the dash length
// 
- (int) dashLength
{
  return _dashLength;
}

//
// Set the length of a dash
// 
// @param length   the dash length
// 
// @return the object
// 
- (DGraphicDrawable *) dashLength :(int) length
{
  if (length < 2)
    WARNING(DW_INVALID_ARG, "length");
  else
    _dashLength = length;
  
  return self;
}


//// Cursor methods

//
// Return the x position of the cursor
// 
// @return the x position
// 
- (unsigned) cursorX
{
  return _cursorX;
}

//
// Return the y position of the cursor
// 
// @return the y position
//
- (unsigned) cursorY
{
  return _cursorY;
}

//
// Set the cursor to a position
// 
// @param x     the x-position
// @param y     the y-position
// 
// @return success
// 
- (BOOL) cursor :(unsigned) x :(unsigned) y
{
  BOOL ok = NO;
  
  if (x > _maxX)
  {
    WARNING(DW_INVALID_ARG, "x");
  }
  else if (y > _maxY)
  {
    WARNING(DW_INVALID_ARG, "y");
  }
  else
  {
    _cursorX = x;
    _cursorY = y;
    
    ok = YES;
  }
  
  return ok;
}

//
// Set the visibility of the cursor
// 
// @param state   the cursor state (DDW_CURSOR..)
// 
// @return the previous state of the cursor
// 
- (int) cursor :(int) state
{
  WARNING(DW_METHOD_NOT_IMPL, "cursor");
  
  return DDW_CURSOR_OFF;
}

//
// Set the visibility of the mouse pointer
// 
// @param state   the pointer state (DDW_POINTER..)
// 
// @return the previous state of the pointer
// 
- (int) pointer :(int) state
{
  WARNING(DW_METHOD_NOT_IMPL, "pointer");
  
  return DDW_POINTER_OFF;
}

//
// Return the last error
// 
// @return the last error (0)
// 
- (int) error
{
  return 0;
}

//
// Tell the current position in the writeable
//
// @return the current position
//
- (unsigned long) tell
{
  return _cursorX + (_maxX+1) * _cursorY;
}

//
// Move the current position to a location
//
// @param offset   the offset from the origin (in positions)
// @param origin   the origin for the offset (0=begin, 1=current, 2=end)
//
// @return success
//

- (BOOL) seek :(unsigned long) offset :(int) origin
{
  BOOL ok = YES;
  
  unsigned long position = 0L;
  unsigned long end      = (_maxX+1) * (_maxY+1) - 1;
  
  switch (origin)
  {
   case 0:
     position = offset;
     break;
    
   case 1:
     position = [self tell] + offset;
     break;
    
   case 2:
     if (offset <= end)
     {
       position = end - offset;
     }
     else
       ok = NO;
     break;

   default:
     WARNING(DW_INVALID_ARG, "origin");
     ok = NO;
     break;
  }
  
  if (ok)
  {
    if (position <= end)
    {
      ok = [self cursor :(position % (_maxX+1)) :(position / (_maxX+1))];
    }
    else
      ok = NO;
  }

  return ok;
}

//
// Skip a number of positions
//
// @param offset   the number of positions to skip
//
// @return success
//
- (BOOL) skip :(unsigned long) offset
{
  BOOL ok = NO;
  
  unsigned long position = [self tell] + offset;
  
  if (position < ((_maxX+1) * (_maxY+1) - 1))
  {
    [self cursor :(position % (_maxX+1)) :(position / (_maxX+1))];
    
    ok = YES;
  }

  return ok;
}


//// Font methods

//
// Set the current font
//
// @param font     the (reference to the) font (or nil for none)
//
// @return the (reference to the) previous font
//
- (id <DFont>) font :(id <DFont>) font
{
  id <DFont> previous = _font;
  
  _font = font;
  
  return previous;
}

//
// Get the current font
//
// @return the (reference to the) current font (or nil)
//
- (id <DFont>) font
{
  return _font;
}

//// Surface methods

//
// Return the maximum x-position
// 
// @return the maximum x-position
//
- (unsigned) maxX
{
  return _maxX;
}

//
// Return the height
// 
// @return the height
// 
- (unsigned) height
{
  return _maxY+1;
}

//
// Return the maximum y-position
// 
// @return the maximum y-position
// 
- (unsigned) maxY
{
  return _maxY;
}

//
// Return the width
// 
// @return the width
// 
- (unsigned) width
{
  return _maxX+1;
}

//
// Set the dimensions of the drawable
// 
// @param width  the width (> 0)
// @param height the height (> 0)
// 
// @return the object
// 
- (DGraphicDrawable *) drawable :(unsigned) width :(unsigned) height
{
  if (width== 0) 
  {
    WARNING(DW_INVALID_ARG, "columns");
  }
  else if (height == 0)
  {
    WARNING(DW_INVALID_ARG, "lines");
  }
  else
  {
    _maxX = width-1;
    _maxY = height-1;
    
    if (_clipMaxX > _maxX) _clipMaxX = _maxX;
    if (_clipMaxY > _maxY) _clipMaxY = _maxY;
  }
  
  return self;
}

//
// Check if a cursor position is valid (within the drawable)
// 
// @param x     the x-position
// @param y     the y-position
// 
// @return is it ?
// 
- (BOOL) isValid :(unsigned) x :(unsigned) y
{
  return ((x <= _maxX) && (y <= _maxY));
}


//// Clipping methods

//
// Set a clipping area
// 
// @param minX    the left x-position of the clip area
// @param maxX    the right x-position of the clip area
// @param minY    the top y-position of the clip area
// @param maxY    the bottom y-position of the clip area
// 
// @return success
// 
- (BOOL) clip :(unsigned) minX :(unsigned) minY :(unsigned) maxX :(unsigned) maxY
{
  BOOL ok = NO;

  if (minX > maxX)
  {
    WARNING(DW_INVALID_ARG, "minX");
  }
  else if (minY > maxY)
  {
    WARNING(DW_INVALID_ARG, "minY");
  }
  else
  {
    _clipMinX = minX;
    _clipMinY = minY;
    _clipMaxX = maxX;
    _clipMaxY = maxY;
    
    if (_clipMaxX > _maxX) _clipMaxX = _maxX;
    if (_clipMaxY > _maxY) _clipMaxY = _maxY;
    
    ok = YES;
  }
  
  return ok;
}

//
// Reset the clipping area to the full surface
// 
// @return success
// 
- (BOOL) clip
{
  _clipMinX = 0;
  _clipMaxX = _maxX;
  _clipMinY = 0;
  _clipMaxY = _maxY;
  
  return YES;
}

//
// Return the top position of the clip area
// 
// @return the top y-position
// 
- (unsigned) clipMinY
{
  return _clipMinY;
}

//
// Return the bottom position of the clip area
// 
// @return the bottom y-position
// 
- (unsigned) clipMaxY
{
  return _clipMaxY;
}

//
// Return the left position of the clip area
// 
// @return the left x-position
// 
- (unsigned) clipMinX
{
  return _clipMinX;
}

//
// Return the right position of the clip area
// 
// @return the right x-position
// 
- (unsigned) clipMaxX
{
  return _clipMaxX;
}


//// Color methods

//
// Set the fore- and background color
// 
// @param fgc     the foreground color
// @param bgc     the background color
// 
// @return success
// 
- (BOOL) color :(DColor *) fgc :(DColor *) bgc
{
  BOOL ok = NO;

  if (fgc == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "fgc");
  }
  else if (bgc == nil)
  {
    WARNING(DW_NIL_NOT_ALLOWED, "bgc");
  }
  else
  {
    [_fgc move :fgc];
    [_bgc move :bgc];
    
    ok   = YES;
  }

  return ok;
}

//
// Set the foreground color
// 
// @param fgc     the foreground color
// 
// @return success
// 
- (BOOL) foregroundColor :(DColor *) fgc
{
  return [self color :fgc :_bgc];
}

//
// Get the foreground color
// 
// @return a reference to the foreground color
// 
- (DColor *) foregroundColor
{
  return _fgc;
}

//
// Set the background color
// 
// @param bgc     the background color
// 
// @return success
// 
- (BOOL) backgroundColor :(DColor *) bgc
{
  return [self color :_fgc :bgc];
}

//
// Get the background color
// 
// @return a reference to the background color
// 
- (DColor *) backgroundColor
{
  return _bgc;
}


//// Drawing control methods

//
// Start drawing on the screen
// 
// @return success
// 
- (BOOL) startDrawing
{
  SDL_Surface *surface = _surface;
  
  if (_surface == NULL)
  {
    WARNING(DW_INVALID_STATE, "open");
  }
  else if (!_drawing)
  {
    _drawing = YES;
  
    if (SDL_MUSTLOCK(surface))
    {
      SDL_LockSurface(_surface);
    }
  }
  
  return [self clip];
}

//
// Start drawing in a clipped area on the screen
// 
// @param minX      the left x-position of the clipping area
// @param maxX      the right x-position of the clipping area
// @param minY      the top y-position of the clipping area
// @param maxY      the bottom y-position of the clipping area
// 
// @return success
//
- (BOOL) startDrawing :(unsigned) minX :(unsigned) minY :(unsigned) maxX :(unsigned) maxY
{
  BOOL ok = [self startDrawing];
  
  if (ok)
  {
    ok = [self clip :minX :minY :maxX :maxY];
  }
  
  return ok;
}

//
// Stop drawing on the screen
// 
// @return the screen object
// 
- (DGraphicDrawable *) stopDrawing
{
  SDL_Surface *surface = _surface;

  if (_drawing)
  {
    _drawing = NO;
  
    if (SDL_MUSTLOCK(surface))
    {
      SDL_UnlockSurface(surface);
    }
  }
  
  return self;
}

//
// Check if the screen is in drawing mode
// 
// @return is it ?
// 
- (BOOL) isDrawing
{
  return _drawing;
}


//// Drawing methods

//
// Clear the drawable (in background color)
// 
// @return success
// 
- (BOOL) clear
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else
  {
    SDL_Rect rect;
    
    rect.x = _clipMinX;
    rect.y = _clipMinY;
    rect.w = (_clipMaxX - _clipMinX + 1);
    rect.h = (_clipMaxY - _clipMinY + 1);

    SDL_FillRect(_surface, &rect, _color2SDL(_surface, _bgc));
  }
  
  return ok;
}

#if _PRIVATE_

//
// Write a character on the screen on the current cursor position, font and color
// 
// @param ch       the character
// 
// @return success
// 
- (BOOL) _putChar :(char) ch
{
  id <DFont> font = (_font == nil) ? (id <DFont>) _basicFont : _font;
  DFT_Glyph  glyph;
  
  BOOL ok = [font glyph :ch :&glyph];

  if (ok)
  {
    SDL_Surface *surface = _surface;
    Uint32       color   = _color2SDL(surface, _fgc);
    Uint8        alpha   = [_fgc alpha];
      
    unsigned xs = _cursorX + glyph.offsetX;
    unsigned xe = xs + glyph.bitmapWidth -1;
    unsigned ys = _cursorY + glyph.ascender - glyph.offsetY;
    unsigned ye = ys + glyph.bitmapHeight - 1;
    unsigned y  = ys;
    
    unsigned char *bitmap = glyph.bitmap;
    
    _cursorX += glyph.advanceX - 1;

    if (_cursorY + glyph.height > _nextY)
    {
      _nextY = _cursorY + glyph.height;
    }

    while (y <= ye)
    {
      if ((y >= _clipMinY) && (y <= _clipMaxY))
      {
        unsigned          x = xs;
        unsigned char  mask = 0x80;
        unsigned char *srce = bitmap;
        
        while (x <= xe)
        {
          if ((*srce & mask) && (x >= _clipMinX) && (x <= _clipMaxX))
          {
            ok &= _putPixel(surface, x, y, color, alpha);
          }
          
          mask >>= 1;
          if (mask == 0)
          {
            mask = 0x80;
            srce++;
          }
          x++;
        }
      }
      bitmap += glyph.bitmapPitch;
      y++;
    }
  }
  return ok;
}

#endif

//
// Write a text string on the screen on the current cursor position
// 
// @param text    the text string
// 
// @return success
// 
- (BOOL) writeText :(const char *) text
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (text == NULL)
  {
    WARNING(DW_INVALID_ARG, "text");
  }
  else
  {
    _nextY = 0;
    
    while (*text != EOS)
    {
      ok &= [self _putChar :*text++];
    }
  }
  
  return ok;
}

//
// Write a character on the screen on the current cursor position
// 
// @param ch      the character
// 
// @return success
// 
- (BOOL) writeChar :(char) ch
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else
  {
    ok = [self _putChar :ch];
  }
  
  return ok;
}

//
// Write a text line on the screen on the current cursor position
// 
// @param text    the text string
// 
// @return success
// 
- (BOOL) writeLine :(const char *) text
{
  BOOL ok = [self writeText :text];
  
  if (ok)
  {
    _cursorX = 0;
    
    _cursorY = _nextY;
  }
  
  return ok;
}

//
// Write a text string on the screen
// 
// @param startX  the start x position
// @param startY  the start y position
// @param text    the text string
// 
// @return success
// 
- (BOOL) writeText :(unsigned) startX :(unsigned) startY :(const char *) text
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (text == NULL)
  {
    WARNING(DW_INVALID_ARG, "text");
  }
  else if ([self cursor :startX :startY])
  {
    _nextY = 0;
    
    while (*text != EOS)
    {
      ok &= [self _putChar :*text++];
    }
  }
  
  return ok;
}

//
// Write a character on the screen
// 
// @param startX  the start x position
// @param startY  the start y position
//
// @param ch      the character
// 
// @return success
// 
- (BOOL) writeChar :(unsigned) startX :(unsigned) startY :(char) ch
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if ([self cursor :startX :startY])
  {
    ok &= [self _putChar :ch];
  }
  
  return ok;
}


//// Image methods

//
// Write an image to the drawable
// 
// @param startX   the start x position
// @param startY   the start y position
// @param image    the source image object
// 
// @return success
// 
- (BOOL) writeImage :(unsigned) startX :(unsigned) startY :(id <DImage>) image
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (image == nil)
  {
    WARNING(DW_INVALID_ARG, "image");
  }
  else if (([image bytesPerPixel] != 3) && ([image bytesPerPixel] != 4))
  {
    WARNING(DW_INVALID_ARG, "writeImage only supports 3 or 4 bytesPerPixel");
  }
  else if ([self cursor :startX :startY])
  {
    unsigned     sx         = _cursorX;
    unsigned     sy         = _cursorY;
    unsigned     clipMinX   = _clipMinX;
    unsigned     clipMaxX   = _clipMaxX;
    unsigned     clipMinY   = _clipMinY;
    unsigned     clipMaxY   = _clipMaxY;
    int          step;
    unsigned     images     = [image images];
    unsigned     width      = [image width];
    unsigned     height     = [image height];
    SDL_Surface *surface    = _surface;
    unsigned     bpp        = [image bytesPerPixel];
    
    unsigned char *data = (unsigned char *) objc_malloc(sizeof(unsigned char) * bpp * width);

    ok = YES;
    
    for (step = 0; step < images; step++)
    {
      for (sy = _cursorY; sy < (_cursorY + height); sy++)
      {
        ok &= ([image readRow :data] > 0);
        
        if ((sy >= clipMinY) && (sy <= clipMaxY))
        {
          unsigned char *src = data;

          if (bpp == 3)
          {
            for (sx = _cursorX; sx < (_cursorX + width); sx++)
            {
              if ((sx >= clipMinX) && (sx <= clipMaxX))
              {
                Uint32 color = SDL_MapRGB(surface->format, *src, *(src+1), *(src+2));
              
                _putPixel(surface, sx, sy, color, 255);
              }
              src += 3;
            }
          }
          else // bpp == 4
          {
            for (sx = _cursorX; sx < (_cursorX + width); sx++)
            {
              if ((sx >= clipMinX) && (sx <= clipMaxX))
              {
                Uint32 color = SDL_MapRGB(surface->format, *src, *(src+1), *(src+2));
              
                _putPixel(surface, sx, sy, color, *(src+3));
              }
              src += 4;
            }
          }
        }
      }
    }
    objc_free(data);
  }
  
  return ok;
}
  
//
// Read an image from the drawable
// 
// @param startX     the start x position
// @param startY     the start y position
// @param image      the destination image object
// 
// @return success
// 
- (BOOL) readImage :(unsigned) startX :(unsigned) startY :(id <DImage>) image
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (image == nil)
  {
    WARNING(DW_INVALID_ARG, "image");
  }
  else if (([image bytesPerPixel] != 3) && ([image bytesPerPixel] != 4))
  {
    WARNING(DW_INVALID_ARG, "readImage only supports 3 or 4 bytesPerPixel");
  }
  else if ([self cursor :startX :startY])
  {
    unsigned     sx         = _cursorX;
    unsigned     sy         = _cursorY;
    unsigned     width      = [image width];
    unsigned     height     = [image height];
    SDL_Surface *surface    = _surface;
    unsigned     bpp        = [image bytesPerPixel];
    
    unsigned char *data = (unsigned char *) objc_malloc(sizeof(unsigned char) * bpp * width);

    ok = YES;
    
    for (sy = _cursorY; sy < (_cursorY + height); sy++)
    {
      unsigned char *dest = data;
      
      if (bpp == 3)
      {
        for (sx = _cursorX; sx < (_cursorX + width); sx++)
        {
          Uint32 color = _getPixel(surface, sx, sy);
        
          SDL_GetRGB(color, surface->format, dest, dest+1, dest+2);
        
          dest += 3;
        }
      }
      else // bpp == 4
      {
        for (sx = _cursorX; sx < (_cursorX + width); sx++)
        {
          Uint32 color = _getPixel(surface, sx, sy);
        
          SDL_GetRGB(color, surface->format, dest, dest+1, dest+2);

          *(dest+3) = 255;
          
          dest += 4;
        }
      }
      
      ok &= [image writeRow :data :width];
    }
    
    objc_free(data);
  }
  
  return ok;
}


#if _PRIVATE_

//
// Draw horizontal line
// 
// @param self    the graphic drawable object
// @param ex      the end x-position
// 
// @return success
// 
static BOOL _drawHLine(DGraphicDrawable *self, unsigned ex)
{
  BOOL         ok         = YES;

  SDL_Surface *surface    = self->_surface;
  Uint32       color      = _color2SDL(surface, self->_fgc);
  Uint8        alpha      = [self->_fgc alpha];
  
  unsigned     sx         = self->_cursorX;
  unsigned     sy         = self->_cursorY;
  unsigned     clipMinX   = self->_clipMinX;
  unsigned     clipMaxX   = self->_clipMaxX;
  int          length     = 0;
  int          diff       = 0;
  
  int          lineType   = self->_lineType;
  float        dashLength = (lineType == DDW_LINE_DASHED ? (float) self->_dashLength : 1.0);
  
  if (sx < ex)
  {
    length = (ex - sx + 1);
    diff   = 1;
  }
  else
  {
    length = (sx - ex + 1);
    diff   = -1;
  }
  
  if ((lineType == DDW_LINE_DOTTED) || (lineType == DDW_LINE_DASHED))
  {
    if (length < (int) (dashLength * 3.0))
    {
      lineType = DDW_LINE_SOLID;
    }
  }
  
  if ((lineType == DDW_LINE_DOTTED) || (lineType == DDW_LINE_DASHED))
  {
    float dashes = 0.0;
    float part   = 0.0;
    BOOL  dash   = YES;
    
    dashes       = (float) length / (2.0 * dashLength);
    dashes       = floor(dashes) + 0.5;
    dashLength   = ((float) length / dashes) / 2.0;

    while (length > 0)
    {
      if ((dash) && (sx >= clipMinX) && (sx <= clipMaxX))
      {
        ok &= _putPixel(surface, sx, sy, color, alpha);
      }
      length--;
      
      self->_cursorX = sx;
      
      sx += diff;
      
      part++;
      if (part >= dashLength)
      {
        part -= dashLength;
        dash  = !dash;
      }
    }
  }
  else
  {
    while (length > 0)
    {
      if ((sx >= clipMinX) && (sx <= clipMaxX))
      {
        ok &= _putPixel(surface, sx, sy, color, alpha);
      }
      length--;

      self->_cursorX = sx;

      sx += diff;
    }
  }

  return ok;
}

//
// Draw a vertical line
// 
// @param self    the graphic drawable object
// @param ey      the end y-position
// 
// @return success
// 
static BOOL _drawVLine(DGraphicDrawable *self, unsigned ey)
{
  BOOL     ok          = YES;
  
  unsigned sx          = self->_cursorX;
  unsigned sy          = self->_cursorY;
  unsigned clipMinY    = self->_clipMinY;
  unsigned clipMaxY    = self->_clipMaxY;
  int      length      = 0;
  int      diff        = 0;
  
  SDL_Surface *surface = self->_surface;
  
  Uint32   color       = _color2SDL(surface, self->_fgc);
  Uint8    alpha       = [self->_fgc alpha];
  
  int      lineType    = self->_lineType;
  float    dashLength  = (lineType == DDW_LINE_DASHED ? (float) self->_dashLength : 1.0);
  
  if (sy < ey)
  {
    length = (ey - sy + 1);
    diff   = 1;
  }
  else
  {
    length = (sy - ey + 1);
    diff   = -1;
  }
  
  if ((lineType == DDW_LINE_DOTTED) || (lineType == DDW_LINE_DASHED))
  {
    if (length < (int)(dashLength * 3.0))
    {
      lineType = DDW_LINE_SOLID;
    }
  }
  
  
  if ((lineType == DDW_LINE_DOTTED) || (lineType == DDW_LINE_DASHED))
  {
    float dashes = 0.0;
    float part   = 0.0;
    BOOL  dash   = YES;
    
    dashes       = (float) length / (2.0 * dashLength);
    dashes       = floor(dashes) + 0.5;
    dashLength   = ((float) length / dashes) / 2.0;

    while (length > 0)
    {
      if ((dash) && (sy >= clipMinY) && (sy <= clipMaxY))
      {
        ok &= _putPixel(surface, sx, sy, color, alpha);
      }
      length--;
      
      self->_cursorY = sy;
      
      sy += diff;
      
      part++;
      if (part >= dashLength)
      {
        part -= dashLength;
        dash  = !dash;
      }
    }
  }
  else
  {
    while (length > 0)
    {
      if ((sy >= clipMinY) && (sy <= clipMaxY))
      {
        ok &= _putPixel(surface, sx, sy, color, alpha);
      }
      length--;
      
      self->_cursorY = sy;
      
      sy += diff;
    }
  }
  
  return ok;
}

//
// Draw a line
// 
// @param self    the graphic drawable object
// @param ex      the end x-position
// @param ey      the end y-position
// 
// @return success
// 
static BOOL _drawLine(DGraphicDrawable *self, unsigned ex, unsigned ey)
{
  BOOL         ok         = YES;
  
  SDL_Surface *surface    = self->_surface;
  Uint32       color      = _color2SDL(surface, self->_fgc);
  Uint8        alpha      = [self->_fgc alpha];
  
  unsigned     sx         = self->_cursorX;
  unsigned     sy         = self->_cursorY;
  unsigned     clipMinX   = self->_clipMinX;
  unsigned     clipMaxX   = self->_clipMaxX;
  unsigned     clipMinY   = self->_clipMinY;
  unsigned     clipMaxY   = self->_clipMaxY;
  
  float        posX       = (float) sx;
  float        posY       = (float) sy;
    
  int          dX         = (ex - sx);
  int          dY         = (ey - sy);
  int          dMax       = (abs(dX) > abs(dY) ? abs(dX) : abs(dY));
    
  float        diffX      = (float) dX / (float) (abs(dY));
  float        diffY      = (float) dY / (float) (abs(dX));
    
  int          lineType   = self->_lineType;
  float        dashLength = (lineType == DDW_LINE_DASHED ? (float) self->_dashLength : 1.0);
  
  float        length     = sqrt((float) dX * (float) dX + (float) dY * (float) dY);
  
  if (diffX < -1.0) diffX = -1.0;
  if (diffX >  1.0) diffX =  1.0;
  if (diffY < -1.0) diffY = -1.0;
  if (diffY >  1.0) diffY =  1.0;

  if ((lineType == DDW_LINE_DOTTED) || (lineType == DDW_LINE_DASHED))
  {
    if (length < (int)(dashLength * 3.0))
    {
      lineType = DDW_LINE_SOLID;
    }
  }

  if ((lineType == DDW_LINE_DOTTED) || (lineType == DDW_LINE_DASHED))
  {
    float dashes   = 0.0;
    float dashPart = 0.0;
    float dashStep = 0.0;
    BOOL  dash     = YES;
    
    dashes         = length / (2.0 * dashLength);
    dashes         = floor(dashes) + 0.5;
    dashLength     = ((float) length / dashes) / 2.0;
    dashStep       = sqrt(diffX * diffX + diffY * diffY);

    while (dMax >= 0)
    {
      if ((dash) &&
          (sx >= clipMinX) && (sx <= clipMaxX) &&
          (sy >= clipMinY) && (sy <= clipMaxY))
      {
        ok &= _putPixel(surface, sx, sy, color, alpha);
      }
      
      dashPart += dashStep;
      if (dashPart >= dashLength)
      {
        dash      = !dash;
        dashPart -= dashLength;
      }
      
      self->_cursorX = sx;
      self->_cursorY = sy;
    
      posX += diffX;
      posY += diffY;
      
      sx = (int) (posX + 0.5);
      sy = (int) (posY + 0.5);
    
      dMax--;
    }
  }
  else
  {
    while (dMax >= 0)
    {
      if ((sx >= clipMinX) && (sx <= clipMaxX) &&
          (sy >= clipMinY) && (sy <= clipMaxY))
      {
        ok &= _putPixel(surface, sx, sy, color, alpha);
      }
      
      self->_cursorX = sx;
      self->_cursorY = sy;
    
      posX += diffX;
      posY += diffY;
      
      sx = (int) (posX + 0.5);
      sy = (int) (posY + 0.5);
    
      dMax--;
    }
  }

  return ok;
}
#endif


//
// Draw a (horizontal) line on the drawable
//
// @param startX    the x position for the start of the line
// @param endX      the x position for the end of the line
// @param startY    the y position for the line
// @param lineType  the type of the line DDW_LINE_...
//
// @return success
//
- (BOOL) drawHLine :(unsigned) startX :(unsigned) startY :(unsigned) endX :(int) lineType
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if ((lineType < DDW_LINE_SOLID) || (lineType > DDW_LINE_DOTTED))
  {
    WARNING(DW_INVALID_ARG, "lineType");
  }
  else if (![self isValid :endX :startY])
  {
    WARNING(DW_INVALID_ARG, "endX/startY");
  }
  else if ([self cursor :startX :startY]) 
  {
    _lineType  = lineType;
    
    ok = _drawHLine(self, endX);
  }
  
  return ok;
}

//
// Draw a horizontal line with the previous line width and type settings
//
// @param startX    the x position for the start of the line
// @param endX      the x position for the end of the line
// @param startY    the y position for the line
//
// @return success
//
- (BOOL) drawHLine :(unsigned) startX :(unsigned) startY :(unsigned) endX 
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :endX :startY])
  {
    WARNING(DW_INVALID_ARG, "startX/endX");
  }
  else if ([self cursor :startX :startY])
  {
    ok = _drawHLine(self, endX);
  }
  
  return ok;
}

//
// Draw a (horizontal) line on the drawable starting from the current position and
// with the previous line width and type settings
//
// @param endX      the x position for the end of the line
//
// @return success
//
- (BOOL) drawHLine : (unsigned) endX 
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :endX :_cursorY])
  {
    WARNING(DW_INVALID_ARG, "endX");
  }
  else
  {
    ok = _drawHLine(self, endX);
  }
  
  return ok;
}

//
// Draw a vertical line on the screen
// 
// @param startX      the x position for the line
// @param startY      the y position for the line
// @param endY        the y position for the end of the line
// @param lineType    the type of the line (DDW_LINE_...)
// 
// @return success
// 
- (BOOL) drawVLine :(unsigned) startX :(unsigned) startY :(unsigned) endY :(int) lineType
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if ((lineType < DDW_LINE_SOLID) || (lineType > DDW_LINE_DOTTED))
  {
    WARNING(DW_INVALID_ARG, "lineType");
  }
  else if (![self isValid :startX :endY])
  {
    WARNING(DW_INVALID_ARG, "startX/endY");
  }
  else if ([self cursor :startX :startY]);
  {
    _lineType  = lineType;
    
    ok = _drawVLine(self, endY);
  }
  
  return ok;
}

//
// Draw a vertical line with the current line type and width settings
// 
// @param startX      the x position for the line
// @param startY      the y position for the line
// @param endY        the y position for the end of the line
// 
// @return success
// 
- (BOOL) drawVLine :(unsigned) startX :(unsigned) startY :(unsigned) endY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :startX :endY])
  {
    WARNING(DW_INVALID_ARG, "startX/endY");
  }
  else if ([self cursor :startX :startY])
  {
    ok = _drawVLine(self, endY);
  }
  
  return ok;
}

//
// Draw a (vertical) line on the drawable starting from the current position and
// with the current line width and type settings
//
// @param endY      the y position for the end of the line
//
// @return success
//
- (BOOL) drawVLine :(unsigned) endY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :_cursorX :endY])
  {
    WARNING(DW_INVALID_ARG, "endY");
  }
  else
  {
    ok = _drawVLine(self, endY);
  }
  
  return ok;
}

//
// Draw a line on the screen
//
// @param startX      the x position for the start of the line 
// @param endX        the x position for the end of the line
// @param startY      the y position for the start of the line
// @param endY        the y position for the end of the line
// @param lineType    the type of the line (DDW_LINE_...)
// 
// @return success
// 
- (BOOL) drawLine :(unsigned) startX :(unsigned) startY :(unsigned) endX :(unsigned) endY :(int) lineType
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if ((lineType < DDW_LINE_SOLID) || (lineType > DDW_LINE_DOTTED))
  {
    WARNING(DW_INVALID_ARG, "lineType");
  }
  else if (![self isValid :endX :endY])
  {
    WARNING(DW_INVALID_ARG, "endX/endY");
  }
  else if ([self cursor :startX :startY]);
  {
    _lineType  = lineType;
    
    if (startX == endX)
    {
      ok = _drawVLine(self, endY);
    }
    else if (startY == endY)
    {
      ok = _drawHLine(self, endX);
    }
    else
    {
      ok = _drawLine(self, endX, endY);
    }
  }
  
  return ok;
}
  
//
// Draw a line with the current line type and width settings
//
// @param startX  the x position for the start of the line 
// @param endX    the x position for the end of the line
// @param startY  the y position for the start of the line
// @param endY    the y position for the end of the line
// 
// @return success
// 
- (BOOL) drawLine :(unsigned) startX :(unsigned) startY :(unsigned) endX :(unsigned) endY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :endX :endY])
  {
    WARNING(DW_INVALID_ARG, "endX/endY");
  }
  else if ([self cursor :startX :startY])
  {
    if (startX == endX)
    {
      ok = _drawVLine(self, endY);
    }
    else if (startY == endY)
    {
      ok = _drawHLine(self, endX);
    }
    else
    {
      ok = _drawLine(self, endX, endY);
    }
  }
  
  return ok;
}

//
// Draw a line on the drawable starting from the current position and
// with the current line width and type settings
//
// @param endX      the x-position for the end of the line
// @param endY      the y-position for the end of the line
//
// @return success
//
- (BOOL) drawLine :(unsigned) endX :(unsigned) endY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :endX :endY])
  {
    WARNING(DW_INVALID_ARG, "endX/endY");
  }
  else  if (_cursorX == endX)
  {
    ok = _drawVLine(self, endY);
  }
  else if (_cursorY == endY)
  {
    ok = _drawHLine(self, endX);
  }
  else
  {
    ok = _drawLine(self, endX, endY);
  }
  
  return ok;
}


//
// Draw a bezier line on the screen
//
// @param startX      the x position for the start of the line 
// @param startY      the y position for the start of the line
// @param strrcX      the x position for the start direction point
// @param strrcY      the y position for the start direction point
// @param endrcX      the x position for the end direction point
// @param endrcY      the y position for the end direction point
// @param endX        the x position for the end of the line
// @param endY        the y position for the end of the line
// 
// @return success
// 
- (BOOL) drawBezierLine :(unsigned) startX :(unsigned) startY 
                        :(unsigned) strrcX :(unsigned) strrcY
                        :(unsigned) endrcX :(unsigned) endrcY
                        :(unsigned) endX   :(unsigned) endY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (![self isValid :startX :startY])
  {
    WARNING(DW_INVALID_ARG, "startX/startY");
  }
  else if (![self isValid :endX :endY])
  {
    WARNING(DW_INVALID_ARG, "endX/endY");
  }
  else if ([self cursor :startX :startY]);
  {
    double step = 1.0 / (double) _bezierSteps;
    double ts;
    
    for (ts = step; ts < 1.0; ts += step)
    {
      double tw = 1.0 - ts;
      double ta =       tw * tw * tw;
      double tb = 3.0 * ts * tw * tw;
      double tc = 3.0 * ts * ts * tw;
      double td =       ts * ts * ts;
      
      double tx = ta * startX + tb * strrcX + tc * endrcX + td * endX;
      double ty = ta * startY + tb * strrcY + tc * endrcY + td * endY;
      
      ok &= _drawLine(self, (unsigned) tx, (unsigned) ty);
    }
  }
  
  return ok;
}


//
// Draw a point on the drawable
// 
// @param startX   the x-position
// @param startY   the y-position
// 
// @return success
// 
- (BOOL) drawPoint :(unsigned) startX :(unsigned) startY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if ([self cursor :startX :startY])
  {
    if ((startX >= _clipMinX) && (startX <= _clipMaxX) &&
        (startY >= _clipMinY) && (startY <= _clipMaxY))
    {
      ok = _putPixel(_surface, startX, startY, _color2SDL(_surface, _fgc), [_fgc alpha]);
    }
      
    _cursorX++;
  }
  
  return ok;
}

//
// Draw a point on the current position
// 
// @return success
// 
- (BOOL) drawPoint
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else
  {
    if ((_cursorX >= _clipMinX) && (_cursorY <= _clipMaxX) &&
        (_cursorY >= _clipMinY) && (_cursorY <= _clipMaxY))
    {
      ok = _putPixel(_surface, _cursorX, _cursorY, _color2SDL(_surface, _fgc), [_fgc alpha]);
    }
      
    _cursorX++;
  }
  
  return ok;
}

//
// Blit another graphic drawable on this graphic drawable (without alpha blending)
// 
// @param startX    the start x-position where the info is placed on this drawable
// @param startY    the start y-position where the info is placed on this drawable
// @param other     the other drawable
// @param oStartX   the start x-position on the other drawable
// @param oStartY   the start y-position on the other drawable
// @param oEndX     the end x-position on the other drawable
// @param oEndY     the end y-position on the other drawable
// 
// @return success
// 
- (BOOL) blit :(unsigned) startX :(unsigned) startY 
              :(DGraphicDrawable *) other :(unsigned) oStartX :(unsigned) oStartY :(unsigned) oEndX :(unsigned) oEndY
{
  BOOL ok = NO;
  
  if (!_drawing)
  {
    WARNING(DW_INVALID_STATE, "startDrawing");
  }
  else if (other == nil)
  {
    WARNING(DW_INVALID_ARG, "other");
  }
  else if (![other isValid :oStartX :oStartY])
  {
    WARNING(DW_INVALID_ARG, "oStartX/oStartY");
  }
  else if (![other isValid :oEndX :oEndY])
  {
    WARNING(DW_INVALID_ARG, "oEndX/oEndY");
  }
  else if (![self isValid :startX :startY])
  {
    WARNING(DW_INVALID_ARG, "startX/startY");
  }
  else
  {
    SDL_Rect srce;
    SDL_Rect dest;
    
    if (oStartX < oEndX)
    {
      srce.x = oStartX;
      srce.w = oEndX - oStartX + 1;
    }
    else
    {
      srce.x = oEndX;
      srce.w = oStartX - oEndX + 1;
    }
    
    if (oStartY < oEndY)
    {
      srce.y = oStartY;
      srce.h = oEndY - oStartY + 1;
    }
    else
    {
      srce.y = oEndY;
      srce.h = oStartY - oEndY + 1;
    }

    dest.x = startX;
    dest.y = startY;
    
    ok = (SDL_BlitSurface(other->_surface, &srce, _surface, &dest) == 0);
  }
  
  return ok;
}


//// Private methods


@end

#endif
  
  
  
  
  
#if _INTERFACE_

#ifdef HAVE_DGRAPHICSCREEN


//
// The DGraphicScreen class implements a class to use the graphic screen. It
// is a wrapper for the sdl library. For obvious reasons there can be only
// one graphic screen object in any application.
// 
// @example
// #include <stdio.h>
// #include "ofc/DGraphicScreen.h"
// #include "ofc/DTrueTypeFont.h"
// #include "ofc/DPNGImage.h"
// #include "ofc/DFile.h"
// 
// @interface MyScreen : DGraphicScreen <DScreenHandler>  // Class for the screen and its events
// {
//   DColor          *_fgc;
//   DColor          *_bgc;
//   DTrueTypeFont   *_font;
//   DGraphicSurface *_image;
// }
// - (MyScreen *) init;
// - (BOOL) open;
// - (void) draw :(const char *) key;
// - (BOOL) keyPress :(DKey *) key :(int) state;      // Callbacks
// - (BOOL) mouseMove :(unsigned) x :(unsigned) y;
// - (BOOL) mouseButtonPress :(DKey *) button :(int) state;
// - (BOOL) screenResize :(unsigned) maxX :(unsigned) maxY;
// - (BOOL) screenFocus :(BOOL) focus;
// - (BOOL) screenRedraw :(unsigned) minX :(unsigned) maxX :(unsigned) minY :(unsigned) maxY;
// @end
// 
// @implementation MyScreen
// 
// - (MyScreen *) init
// {
//   [super init];
//   
//   _fgc  = [DColor new];
//   _bgc  = [DColor new];
//   _font = [DTrueTypeFont new];
//   _image = [DGraphicSurface new];
// 
//   return self;
// }
// 
// - (BOOL) open                          // Open the screen with ..
// {
//   BOOL ok = NO;
// 
//   [_fgc textColor :DCLR_BLACK];       // .. foreground and background color
//   [_bgc textColor :DCLR_WHITE];
//   
//   ok = [self open :600 :400 :0 :_fgc :_bgc];
// 
//   if (ok)
//   {
//     [self screenHandler :self];        // Object is handling its events itself
//   }
// 
//   if (ok)
//   {
//     ok = [_font open :"../test/arial.ttf"]; // Open the arial font
// 
//     if (ok)
//     {
//       [_font size :"a" :15 :15];       // Size the font
// 
//       [self font :_font];              // Make it the default font
//     }
// 
//     if (ok)
//     {
//       DFile *file    = [DFile new];
//       DPNGImage *png = [DPNGImage new];
// 
//       if ([file open :"ofc.png" :"rb"]) // Open the png image file
//       {
//         if ([png open :file])          // Give the file to the png image
//         {                              // Open a graphic surface for the image
//           if ([_image open :[png width] :[png height] :_bgc :_bgc])
//           {
//             [_image startDrawing];
//             [_image writeImage :0 :0 :png]; // Write the image to the surface
//             [_image stopDrawing];
//           }
//           [png close];
//         }
//         [file close];
//        }
//     }
//   }
//   
//   return ok;
// }
// 
// - (void) draw :(const char *) key      // Screen drawing
// {
//   int maxX = [self maxX];              // Dimensions of screen
//   int maxY = [self maxY];
// 
//   char hello[] = "Hello World";
//   char quit[]  = "Press q to quit";
// 
//   unsigned width,height;
// 
//   [self startDrawing];                 // Start drawing
//   [self clear];
//   [self drawHLine :10 :10 :maxX-10];      // Draw a some lines
//   [self drawHLine :maxX-10 :maxY-10 :10];
//                                        // Write some text
//   [_font stringSize :hello :&width :&height];
// 
//   [self cursor :((maxX - width)/2) :((maxY-40)/2)];
//   [self writeText :hello];
// 
//   [_font stringSize :quit :&width :&height];
// 
//   [self cursor :((maxX - width)/2) :((maxY+40)/2)];
//   [self writeText :quit];
// 
//   if (key != NULL)
//   {
//     [self cursor :30 :maxY-30];
//     [self writeText :"Key pressed:"];
//     [self writeText :key];
//   }
//                                        // Blit the image surface on the screen
//   [self blit :10 :15 :_image :0 :0 :[_image maxX] :[_image maxY]];
// 
//   [self stopDrawing];
// }
//                                        // Callback for pressed key
// - (BOOL) keyPress :(DKey *) key :(int) state
// {
//   if ([key key] != 'q')
//   {
//     DText *info = [key toText];
//     
//     [self draw :[info cstring]];
// 
//     [info free];
//   }
//   
//   return ([key key] != 'q');
// }
//                                        // Callback for moved mouse
// - (BOOL) mouseMove :(unsigned) x :(unsigned) y
// {
//   return YES;
// }
//                                        // Callback for pressed mouse button
// - (BOOL) mouseButtonPress :(DKey *) button :(int) state
// {
//   return YES;
// }
//                                        // Callback for screen resize
// - (BOOL) screenResize :(unsigned) maxX :(unsigned) maxY
// {
//   [self draw :NULL];
// 
//   return YES;
// }
//                                        // Callback for lost and gained screen focus
// - (BOOL) screenFocus :(BOOL) focus
// {
//   [self draw :NULL];
// 
//   return YES;
// }
//                                        // Callback for screen redraw
// - (BOOL) screenRedraw :(unsigned) minX :(unsigned) maxX :(unsigned) minY :(unsigned) maxY
// {
//   [self draw :NULL];
// 
//   return YES;
// }
// 
// @end
// 
// int main(int argc, char *argv[])
// {
//   MyScreen *screen = [MyScreen new];
// 
//   if ([screen open])                   // Open the screen
//   {
//     [screen draw :NULL];               // Draw the screen
// 
//     [screen waitEvents];               // Wait for and process events
//   }
//   else
//     printf("Could not open the screen..\n");
// 
//   [screen free];                       // Cleanup
// 
//   return 0;
// }
// 

@interface DGraphicScreen : DGraphicDrawable
{
@private
  id <DScreenHandler>  _handler;     // the screen event handler
  DKey                *_key;         // the key
  unsigned             _mask;        // the current key mask
  int                  _bbp;         // the bits per pixel or 0 for any
}

#endif
#endif




#ifdef HAVE_DGRAPHICSCREEN


@implementation DGraphicScreen

  
#if _PRIVATE_
static SDL_Surface *_screen = NULL;  // the screen surface
#endif


//// Constructors

//
// Initialise to an unopened graphic screen object
//
// @return the object
//
- (DGraphicScreen *) init
{
  [super init];
  
  _key         = [DKey new];
  _handler     = nil;
  _mask        = 0;
  _bbp         = 0;
  
  return self;
}

//
// Initialise and open a graphic screen object with a fore- and background color
// 
// @param width   the width of the screen
// @param height  the height of the screen
// @param bbp     the depth of the screen (0 = current depth)
// @param fgc     the foreground color
// @param bgc     the background color
//
// @return the object
//
- (DGraphicScreen *) init :(unsigned) width :(unsigned) height :(unsigned) bbp :(DColor *) fgc :(DColor *) bgc
{
  [self init];

  [self open :width :height :bbp :fgc :bgc];
  
  return self;
}


//// Copy related methods

//
// Do a shallow copy of the graphic screen object (not implemented)
//
// @return the object
//
- shallowCopy
{
  WARNING(DW_METHOD_NOT_IMPL, "shallowCopy");
  
  return [super shallowCopy];
}

  
//// Deconstructor

//
// Free the graphic screen
// 
// @return the object
// 
- free
{
  [self close];

  _handler = nil;

  [_key free];

  return [super free];
}


//// Screen status methods


//// Event handler methods

//
// Set the screen event handler
// 
// @param handler   the (reference to the) screen handler
// 
// @return the previous screen handler
// 
- (id <DScreenHandler>) screenHandler :(id <DScreenHandler>) handler
{
  id <DScreenHandler> tmp = _handler;
  
  _handler = handler;
  
  return tmp;
}

//
// Return the current screen event handler
// 
// @return the (reference to the) screen handler
// 
- (id <DScreenHandler>) screenHandler
{
  return _handler;
}


//// Screen control methods

//
// Open the graphic screen (only one instance is opened)
//
// @param width   the width of the screen
// @param height  the height of the screen
// @param bbp     the depth of the screen (0 = current depth)
// @param fgc     the foreground color
// @param bgc     the background color
// 
// @return success
// 
- (BOOL) open :(unsigned) width :(unsigned) height :(unsigned) bbp :(DColor *) fgc :(DColor *) bgc
{
  BOOL ok = NO;

  if (_screen != NULL)
  {
    WARNING(DW_UNKNOWN_WARNING, "screen alreay opened");
  }
  else
  {
    if (SDL_Init(SDL_INIT_VIDEO) == 0)
    {
      _bbp = bbp;
      
      [DAtExit add :self];

      _surface = SDL_SetVideoMode(width, height, _bbp, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ANYFORMAT|SDL_RESIZABLE);
      
      if (_surface != NULL)
      {
        SDL_Surface *surface = _surface;
        
        [self drawable :surface->w :surface->h];
        
        _screen = _surface;
        
        ok = YES;
      }
      else
      {
        WARNING(DW_UNKNOWN_WARNING, SDL_GetError());
        
        [self close];
      }
        
    }
    else
    {
      WARNING(DW_UNKNOWN_WARNING, SDL_GetError());
    }
  }
  
  if (ok)
  {
    [self startDrawing];
    [self color :fgc :bgc];
    [self clear];
    [self stopDrawing];
  }

  return ok;
}

// 
// Close the graphic screen
// 
// @return the object
// 
- (DGraphicScreen *) close
{
  SDL_Quit();
  
  [DAtExit remove :self];

  _surface = NULL;
  _screen  = NULL;
  
  return self;
}


//// Screen output methods

//
// Beep the terminal
//
// @return the object
//
- (DGraphicScreen *) beep 
{
  WARNING(DW_METHOD_NOT_IMPL, "beep");
  
  return self;
}


//
// Set the visibility of the mouse pointer
// 
// @param state   the pointer state (DDW_POINTER..)
// 
// @return the previous state of the pointer
// 
- (int) pointer :(int) state
{
  if (state == DDW_POINTER_ON)
  {
    state = SDL_ShowCursor(SDL_ENABLE);
  }
  else if (state == DDW_POINTER_OFF)
  {
    state = SDL_ShowCursor(SDL_DISABLE);
  }
  else
  {
    WARNING(DW_INVALID_ARG, "state");
  }
  
  return (state == SDL_ENABLE ? DDW_POINTER_ON : DDW_POINTER_OFF);
}


//// Drawing control methods

//
// Stop drawing on the screen
// 
// @return the screen object
// 
- (DGraphicScreen *) stopDrawing
{
  if ([self isDrawing])
  {
    [super stopDrawing];

    if (SDL_Flip(_surface) == -1)
    {
      WARNING(DW_UNKNOWN_WARNING, "flip failed");
    }
  }
  
  return self;
}


//// Event processing

#if _PRIVATE_

//
// Translate the SDL event
// 
// @param handler   the screen handler object
// @param obj       the key object
// @param event     the win32 event
//
// @return continue processing events ?
//  
static BOOL _translateEvent(DGraphicScreen *self, id <DScreenHandler> handler, DKey *obj, SDL_Event *event)
{
  BOOL continu = YES;
  int  key     = -1;
        
  switch(event->type)
  {
    case SDL_KEYDOWN:
    case SDL_KEYUP  :
    {
      BOOL   down = (event->type == SDL_KEYDOWN);
      SDLKey sym  = event->key.keysym.sym;
      SDLMod mod  = event->key.keysym.mod;
      
      if ((sym >= ' ') && (sym < 127))
      {
        key = sym;
      }
      else if ((sym >= SDLK_F1) && (sym <= SDLK_F15))
      {
        key = DKEY_F1 + (sym - SDLK_F1);
      }
      else
      {
        switch (sym)
        {
          case SDLK_BACKSPACE   : key = DKEY_BACKSPACE;    break;
          case SDLK_UP          : key = DKEY_UP;           break;
          case SDLK_DOWN        : key = DKEY_DOWN;         break;
          case SDLK_RIGHT       : key = DKEY_RIGHT;        break;
          case SDLK_LEFT        : key = DKEY_LEFT;         break;
          case SDLK_INSERT      : key = DKEY_INSERT;       break;
          case SDLK_DELETE      : key = DKEY_DELETE;       break;
          case SDLK_HOME        : key = DKEY_HOME;         break;
          case SDLK_END         : key = DKEY_END;          break;
          case SDLK_PAGEUP      : key = DKEY_PAGEUP;       break;
          case SDLK_PAGEDOWN    : key = DKEY_PAGEDOWN;     break;
          case SDLK_RETURN      : key = DKEY_ENTER;        break;
          
          case SDLK_NUMLOCK     : key = DKEY_NUMLOCK;      break;
          case SDLK_CAPSLOCK    : key = DKEY_CAPSLOCK;     break;
          case SDLK_SCROLLOCK   : key = DKEY_SCROLLLOCK;   break;
          
          case SDLK_RSHIFT      :
          case SDLK_LSHIFT      : key = DKEY_SHIFT; 
                                  if (down) 
                                    self->_mask |= DKEY_MOD_SHIFT;
                                  else 
                                    self->_mask &= (~DKEY_MOD_SHIFT);
                                  break;
          case SDLK_RCTRL       :
          case SDLK_LCTRL       : key = DKEY_CTRL;
                                  if (down)
                                    self->_mask |= DKEY_MOD_CTRL;
                                  else
                                    self->_mask &= (~DKEY_MOD_CTRL);
                                  break;
          case SDLK_RALT        :
          case SDLK_LALT        : key = DKEY_ALT;
                                  if (down)
                                    self->_mask |= DKEY_MOD_ALT;
                                  else
                                    self->_mask &= (~DKEY_MOD_ALT);
                                  break;
          
          case SDLK_KP0         : key = '0' | DKEY_MOD_KP; break;
          case SDLK_KP1         : key = '1' | DKEY_MOD_KP; break;
          case SDLK_KP2         : key = '2' | DKEY_MOD_KP; break;
          case SDLK_KP3         : key = '3' | DKEY_MOD_KP; break;
          case SDLK_KP4         : key = '4' | DKEY_MOD_KP; break;
          case SDLK_KP5         : key = '5' | DKEY_MOD_KP; break;
          case SDLK_KP6         : key = '6' | DKEY_MOD_KP; break;
          case SDLK_KP7         : key = '7' | DKEY_MOD_KP; break;
          case SDLK_KP8         : key = '8' | DKEY_MOD_KP; break;
          case SDLK_KP9         : key = '9' | DKEY_MOD_KP; break;
          case SDLK_KP_PERIOD   : key = '.' | DKEY_MOD_KP; break;
          case SDLK_KP_DIVIDE   : key = '/' | DKEY_MOD_KP; break;
          case SDLK_KP_MULTIPLY : key = '*' | DKEY_MOD_KP; break;
          case SDLK_KP_MINUS    : key = '-' | DKEY_MOD_KP; break;
          case SDLK_KP_PLUS     : key = '+' | DKEY_MOD_KP; break;
          case SDLK_KP_EQUALS   : key = '=' | DKEY_MOD_KP; break;
          
          case SDLK_KP_ENTER    : key = DKEY_ENTER|DKEY_MOD_KP; break;
          default               : ;
        }
      }
      
      if (key != -1)
      {
        if (mod & KMOD_CTRL)
        {
          key |= DKEY_MOD_CTRL;
        }
        if (mod & KMOD_SHIFT)
        {
          key |= DKEY_MOD_SHIFT;
        }
        if (mod & KMOD_ALT)
        {
          key |= DKEY_MOD_ALT;
        }
      }
      
      if ((key != -1) && (handler != nil))
      {
        [obj set :key];
      
        if (down)
        {
          continu &= [handler keyPress :obj :DSH_KEY_DOWN];
          continu &= [handler keyPress :obj :DSH_KEY_PRESS];
        }
        else
        {
          continu &= [handler keyPress :obj :DSH_KEY_UP];
        }
      }
    }
    break;

    case SDL_ACTIVEEVENT:
    {
      if (handler != nil)
      {
        continu &= [handler screenFocus :(event->active.gain != 0)];
      }
    }
    break;

    case SDL_MOUSEBUTTONDOWN:
    case SDL_MOUSEBUTTONUP  :
    {
      Uint8 button = event->button.button;
      
      switch(button)
      {
        case SDL_BUTTON_LEFT  : key = DKEY_MOUSE_LEFT;   break;
        case SDL_BUTTON_MIDDLE: key = DKEY_MOUSE_MIDDLE; break;
        case SDL_BUTTON_RIGHT : key = DKEY_MOUSE_RIGHT;  break;
      }
      
      if ((key != -1) && (handler != nil))
      {
        [obj set :key|self->_mask];
        
        int state = DSH_BUTTON_NONE;
        
        if (event->type == SDL_MOUSEBUTTONDOWN)
        {
          state = DSH_BUTTON_DOWN;
        }
        else if (event->type == SDL_MOUSEBUTTONUP)
        {
          state = DSH_BUTTON_UP;
        }
        
        Uint16 x = event->motion.x;
        Uint16 y = event->motion.y;
        
        continu &= [handler mouseMove :x :y];
        continu &= [handler mouseButtonPress :obj :state];
        
        if (state == DSH_BUTTON_UP)
        {
          continu &= [handler mouseButtonPress :obj :state];
        }
      }
    }
    break;
    
    case SDL_MOUSEMOTION:
    {
      if (handler != nil)
      {
        Uint16 x = event->motion.x;
        Uint16 y = event->motion.y;
        
        continu &= [handler mouseMove :x :y];
      }
    }
    break;

    case SDL_VIDEORESIZE:
    {
      int width  = event->resize.w;
      int height = event->resize.h;
      
      SDL_Surface *surface = SDL_SetVideoMode(width, height, self->_bbp, SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ANYFORMAT|SDL_RESIZABLE);

      if (surface != NULL)
      {
        self->_surface = surface;
        
        [self drawable :width :height];

        if (handler != nil)
        {
          continu &= [handler screenResize :[self maxX] :[self maxY]];
        }
      }
    }
    break;
  }
  return continu;  
}
#endif

//
// Wait (and process) events
// 
// @return the number of processed events
// 
- (unsigned) waitEvents
{
  unsigned processed = 0;
  
  if (_handler == nil)
  {
    WARNING(DW_OBJECT_NOT_INIT, "screenHandler");
  }
  else
  {
    BOOL continu = YES;

    while (continu)
    {
      SDL_Event event;
      
      if (SDL_WaitEvent(&event) == 1)
      {
        continu = _translateEvent(self, _handler, _key, &event);
        
        processed++;
      }
      else
      {
        WARNING(DW_UNKNOWN_WARNING, SDL_GetError());
        
        continu = NO;
      }
    }
  }
  
  return processed;
}

//
// Process any pending events
//
// @return the number of processed events
// 
- (unsigned) processEvents
{
  unsigned processed = 0;

  if (_handler == nil)
  {
    WARNING(DW_OBJECT_NOT_INIT, "screenHandler");
  }
  else
  {
    BOOL continu       = YES;

    while (continu)
    {
      SDL_Event event;
      
      if (SDL_PollEvent(&event) == 1)
      {
        continu = _translateEvent(self, _handler, _key, &event);
        
        processed++;
      }
      else
      {
        continu = NO;
      }
    }
  }

  return processed;
}

//
// Clear any pending events
//
// @return the object
// 
- (DGraphicScreen *) clearEvents
{
  SDL_Event event;
  
  while (SDL_PollEvent(&event) == 1)
  {
  }
  
  return self;
}

@end

#endif


  
  
  
#if _INTERFACE_

#ifdef HAVE_DGRAPHICSCREEN


//
// The DGraphicSurface class implements a class for off-screen drawing to a graphic
// surface. This surface can then be blit to the graphic screen.
// 

@interface DGraphicSurface : DGraphicDrawable
{
}

#endif
#endif


#ifdef HAVE_DGRAPHICSCREEN


@implementation DGraphicSurface


//// Constructors

//
// Initialise to an graphic surface object
//
// @return the object
//
- (DGraphicSurface *) init
{
  [super init];

  return self;
}


//
// Initialise and open a graphic surface object 
// 
// @param width   the width of the screen
// @param height  the height of the screen
// @param fgc     the (default) foreground color
// @param bgc     the (default) background color
//
// @return the object
//
- (DGraphicSurface *) init :(unsigned) width :(unsigned) height :(DColor *) fgc :(DColor *) bgc
{
  [self init];

  [self open :width :height :fgc :bgc];
  
  return self;
}


//// Copy related methods

//
// Do a shallow copy of the graphic surface object (not implemented)
//
// @return the object
//
- shallowCopy
{
  WARNING(DW_METHOD_NOT_IMPL, "shallowCopy");
  
  return [super shallowCopy];
}

  
//// Deconstructor

//
// Free the graphic surface
// 
// @return the object
// 
- free
{
  [self close];
  
  return [super free];
}


//// Surface control methods

//
// Open a graphic screen surface (with the same depth as the screen)
//
// @param width     the width of the surface
// @param height    the height of the surface
// @param fgc       the (default) foreground color
// @param bgc       the (default) background color
// 
// @return success
// 
- (BOOL) open :(unsigned) width :(unsigned) height :(DColor *) fgc :(DColor *) bgc
{
  BOOL ok = NO;
  
  if (_screen == NULL)
  {
    WARNING(DW_INVALID_STATE, "[DGraphicScreen open]");
  }
  else
  {
    _surface = SDL_CreateRGBSurface(SDL_SWSURFACE, 
                                    width, 
                                    height, 
                                    _screen->format->BitsPerPixel,
                                    _screen->format->Rmask,
                                    _screen->format->Gmask,
                                    _screen->format->Bmask,
                                    _screen->format->Amask);
    
    if (_surface != NULL)
    {
      SDL_Surface *surface = _surface;
      
      [self drawable :surface->w :surface->h];
      
      [self startDrawing];
      [self color :fgc :bgc];
      [self clear];
      [self stopDrawing];
      
      ok = YES;
    }
    else
    {
      WARNING(DW_UNKNOWN_WARNING, SDL_GetError());
    }
  }

  return ok;
}

//
// Close the surface
// 
// @return the object
// 
- (DGraphicSurface *) close
{
  if (_surface != NULL)
  {
    SDL_FreeSurface(_surface);
    
    _surface = NULL;
  }

  return self;
}


@end

#endif

/*===========================================================================*/
